package com.net128.beatportapps.downloader;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.net128.beatportapps.TrackData;

public class BeatportAccessor {
	private final static Logger log = Logger.getLogger(DownloadWorker.class);
	public static boolean debug=false;
	
	private static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
	private final static String base = "://www.beatport.com/en-US/";
	private static String dlTrackInfoUrlBase="http"+base+"xml/user/downloads/available?resultsPerPage=200";
	private static String dlOldTrackInfoUrlBase="http"+base+"xml/user/downloads/redownload";
	private static String debugDlTrackInfoUrlBase="file:/testdata/tracklists/downloaded.xml";
	private static String debugTrackUrl=
		"http://www.net128.com/sounds/trance/cd/sorted/years/1992/cherrymoon%20trax%20-%20let%20there%20be%20house.mp3";

	public String getSessionCookies(String user, String password) {
		String sessionCookies = "";
		String loginUrl = "https" + base + "xml/account/authentication/login";
		loginUrl += "?username=" + user + "&password=" + password;
		HttpClient httpclient = new HttpClient();
		GetMethod httpget = new GetMethod(loginUrl);
		try {
			log.info("Logging in into beatport");
			httpclient.executeMethod(httpget);
			if (httpget.getStatusCode() == 200) {
				DocumentBuilderFactory docfactory = DocumentBuilderFactory.newInstance();
				DocumentBuilder docbuilder = docfactory.newDocumentBuilder();
				//System.out.println(httpget.getResponseBodyAsString());
				Document docroot = docbuilder.parse(httpget.getResponseBodyAsStream());
				XPath xpath = XPathFactory.newInstance().newXPath();
				String msgType = (String) xpath.evaluate("//msg/@type", docroot, XPathConstants.STRING);
				if(!msgType.toLowerCase().equals("success")) {
					log.error("Failed to login: "+user+"/"+password);
					return null;
				}
				
				Cookie[] cookies = httpclient.getState().getCookies();
				int n = 0;
				for (int i = 0; i < cookies.length; i++) {
					Cookie cookie = cookies[i];
					if (cookie.getDomain().indexOf("www.beatport.com") < 0) {
						continue;
					}
					if (n > 0) {
						sessionCookies += "; ";
					}
					sessionCookies += cookie.getName() + "="
							+ cookie.getValue();
					log.info(cookie.getDomain() + " " + cookie.getName() + " "
							+ cookie.getPath() + " " + cookie.getValue());
				}
				httpget.releaseConnection();
				log.info("Login successful");
			}
		} catch(Exception e) {
			log.error("Beatport login error", e);
		} finally {
			httpget.releaseConnection();
		}
		return sessionCookies;
	}

	private InputStream getFileInputStream(String fileUrl) throws Exception {
		if(fileUrl.startsWith("file:/")) {
			return new FileInputStream(fileUrl.substring("file:/".length()));
		}
		return null;
	}
	
	private List<TrackData> getTracks(String sessionCookies, String trackInfoUrlBase) throws Exception {
		InputStream is=getFileInputStream(trackInfoUrlBase);
		if(is!=null) {
			return getTracks(is);
		} else {
			HttpClient httpclient = new HttpClient();
			GetMethod httpget=new GetMethod(trackInfoUrlBase);
			try {
				httpget.addRequestHeader("Cookie", sessionCookies);
				httpclient.executeMethod(httpget);
				if(httpget.getStatusCode()==200) {
					return getTracks(httpget.getResponseBodyAsStream());
				}
				throw new IOException("Unexpected HTTP status code: "+httpget.getStatusCode());
			} finally {
				httpget.releaseConnection();
			}
		}
	}

	public List<TrackData> getAvailableTracks(String sessionCookies) throws Exception {
		if(debug) {
			return getTracks(sessionCookies, debugDlTrackInfoUrlBase);
		}
		return getTracks(sessionCookies, dlTrackInfoUrlBase);
	}

	public List<TrackData> getPreviousTracks(String sessionCookies) throws Exception {
		return getTracks(sessionCookies, dlOldTrackInfoUrlBase);
	}

	private List<TrackData> getTracks(InputStream is)
			throws ParserConfigurationException, SAXException, IOException,
			XPathExpressionException, ParseException {
		DocumentBuilderFactory docfactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder docbuilder = docfactory.newDocumentBuilder();
		Document docroot = docbuilder.parse(is);
		XPath xpath = XPathFactory.newInstance().newXPath();
		NodeList downloadList = (NodeList) xpath.evaluate("//download", docroot,
				XPathConstants.NODESET);

		List<TrackData> trackDataList = new ArrayList<TrackData>();
		int n = downloadList.getLength();
		for (int i = 0; i < n; i++) {
			Element download = (Element) downloadList.item(i);
			Element track = (Element) xpath.evaluate("track", download,
					XPathConstants.NODE);
			String id = track.getAttribute("id");// context.getValue("//track");
			String name = (String) xpath.evaluate("name", track,
					XPathConstants.STRING);
			long size = Long.parseLong((String) download.getAttribute("fileSize"));
			String url = (String) xpath.evaluate("downloadUrl", download,
					XPathConstants.STRING);
			if(debug) {
				url=debugTrackUrl;
				size=10983514;
			}
			String format = (String) xpath.evaluate("format", download,
					XPathConstants.STRING);
			Date dateAdded = dateFormat.parse((String) xpath.evaluate(
					"dateAdded", track, XPathConstants.STRING));
			Date dateReleased = dateFormat.parse((String) xpath.evaluate(
					"dateReleased", track, XPathConstants.STRING));
			String mixName = (String) xpath.evaluate("mixName", track,
					XPathConstants.STRING);
			if ("Original Mix".equals(mixName)) {
				mixName = "";
			}
			mixName = mixName.replaceAll(", ", "+");
			String labelName = (String) xpath.evaluate("label/name", track,
					XPathConstants.STRING);
			NodeList artistList = (NodeList)xpath.evaluate("performer[@ref='Artist']", track, XPathConstants.NODESET);
			Element artist = (Element) artistList.item(0);
			String artistName=artist.getTextContent().trim();
			for(int j=1;j<artistList.getLength();j++) {
				artist = (Element) artistList.item(j);
				artistName+="+"+artist.getTextContent().trim();
			}
			TrackData trackData = new TrackData(id, artistName, name, mixName,
					dateAdded, dateReleased, labelName, url, format,size);
			//log.info(trackData);
			trackDataList.add(trackData);
		}
		try {
			is.close();
		} catch(Exception e) {
			log.warn("Could not close input stream", e);
		}
		return trackDataList;
	}
}
